package simmar.xcs;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import simmar.utilities.Logger;
import simmar.xcs.XCSClassifier;
import simmar.xcs.XCSSubsumption;

public class XCSReinforcement implements XCSConfig
{
    protected static void updateActionSet(float P, List<XCSClassifier> actionSet, Map<String, XCSClassifier> population)
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS UPDATE ACTION SET ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");

        // Calculate sum for action size estimate update
        int numerositySum = 0;

        for (int i = 0; i < actionSet.size(); i++)
        {
            numerositySum += actionSet.get(i).getNumerosity();
        }

        // Update each classifier
        for (int i = 0; i < actionSet.size(); i++)
        {
            actionSet.get(i).incrementExperience();
            int experience = actionSet.get(i).getExperience();
            float prediction = actionSet.get(i).getPrediction();

            // Update prediction
            if (experience < (1 / LEARNING_RATE))
                actionSet.get(i).setPrediction(prediction + (P - prediction) / experience);
            else
                actionSet.get(i).setPrediction(prediction + LEARNING_RATE * (P - prediction));

            float accuracy = actionSet.get(i).getAccuracy();
            prediction = actionSet.get(i).getPrediction();

            // Update accuracy
            if (experience < (1 / LEARNING_RATE))
                actionSet.get(i).setAccuracy(accuracy + (Math.abs(P - prediction) - accuracy) / experience);
            else
                actionSet.get(i).setAccuracy(accuracy + LEARNING_RATE * (Math.abs(P - prediction) - accuracy));

            int actionSetSize = actionSet.get(i).getActionSetSize();

            // Update action set size estimate
            if (experience < (1 / LEARNING_RATE)) // TODO: CHECK UP ON FOR
                                                  // INTEGER/FLOAT DIVISION
                                                  // HERE
                actionSet.get(i).setActionSetSize(Math.round((actionSetSize + ((float) (numerositySum - actionSetSize) / experience))));
            else
                actionSet.get(i).setActionSetSize(Math.round(actionSetSize + LEARNING_RATE * (numerositySum - actionSetSize)));
        }
        
        // Update fitness
        updateFitness(actionSet);
        
//        // Update classifiers in population
//
//        for (int i = 0; i < actionSet.size(); i++)
//        {
//            if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
//            {
//                Logger.logLine("Replacing classifier in population:\n");
//                Logger.log(actionSet.get(i).toString() + "\n");
//            }
//            
//            if (!population.containsKey(actionSet.get(i).getRule().toString()))
//                    Logger.logLine("MEGAFAIL!!!!!!!!!!!!!!!!!!!!!");
//            
//            population.remove(actionSet.get(i).getRule().toString());
//            population.put(actionSet.get(i).getRule().toString(), actionSet.get(i));  
//        }        

        if (DO_ACTION_SET_SUBSUMPTION)
            XCSSubsumption.doActionSetSubsumption(actionSet, population);

        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
        {
            Logger.logLine("Updated action set:\n\n");
            
            for (int i = 0; i < actionSet.size(); i++)
                Logger.log(actionSet.get(i).toString() + "\n\n");           
        }
    }

    protected static void updateFitness(List<XCSClassifier> actionSet)
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS UPDATE FITNESS ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");

        float accuracySum = 0.0f;
        float[] accuracyVector = new float[actionSet.size()];

        for (int i = 0; i < actionSet.size(); i++)
        {
            float accuracy = actionSet.get(i).getAccuracy();

            if (accuracy < ACCURACY_THRESHOLD)
            {
                accuracyVector[i] = 1.0f;
            } else
            {
                accuracyVector[i] = ACCURACY_DISTINCTION_RATE * (float)Math.pow((accuracy / ACCURACY_THRESHOLD), -FITNESS_UPDATE_POWER);
            }

            accuracySum += accuracyVector[i] * actionSet.get(i).getNumerosity();
        }

        for (int i = 0; i < actionSet.size(); i++)
        {
            float fitness = actionSet.get(i).getFitness();
            int numerosity = actionSet.get(i).getNumerosity();
            actionSet.get(i).setFitness(fitness + LEARNING_RATE * (accuracyVector[i] * numerosity / accuracySum - fitness));
        }

//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS UPDATE FITNESS DONE ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");
    }
}